package win.bigwhale.sudoku.view;

import java.awt.Point;
import java.io.File;
import java.util.Optional;

import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import win.bigwhale.sudoku.SudokuUtil;
import win.bigwhale.sudoku.controller.GameControlInterface;
import win.bigwhale.sudoku.controller.GameController;
import win.bigwhale.sudoku.game.Sudoku;
import win.bigwhale.sudoku.logic.SudokuMatrix;
import win.bigwhale.sudoku.logic.SudokuNumber;
import win.bigwhale.sudoku.sound.SoundPlayer;
import win.bigwhale.sudoku.sound.SoundPlayerImp;

public class Playfield extends Pane implements GameControlInterface {
	private static Playfield playfield = new Playfield();
	private SelectedRect selectedRect = null;
	private SudokuMatrix sudokuMatrix;
	private Sudoku game = null;
	private SoundPlayer backgroundSoundPlayer = null;
	
	private Playfield() {
		GameController.getGameController().addPane(this);
		setPrefSize(SudokuUtil.PLAY_FIELD_WIDTH, SudokuUtil.PLAY_FIELD_WIDTH);
		setLayoutX(SudokuUtil.GAME_COMPONENT_MARGIN);
		setLayoutY(SudokuUtil.GAME_COMPONENT_MARGIN);
		
		selectedRect = SelectedRect.getSelectedRectShow();
		getChildren().add(selectedRect.getRectangle());
		selectedRect.setVisible(false);
		sudokuMatrix = SudokuMatrix.getSudokuMatrix();
		
		paintPlayGrid();

		setOnKeyReleased(new TypeNumberEventHandler());
		setOnMouseClicked(new SelectEventHandler());

		backgroundSoundPlayer = new SoundPlayerImp(new MediaPlayer(new Media(new File(SudokuUtil.BACKGROUND_SOUND_PATH).toURI().toString())));
	}

	public static Playfield getPlayField() {
		return playfield;
	}

	private void addSudokuNumberFromString(String str) {
		Point numberPosition = new Point();
		for (int i = 0; i < str.length(); i++) {
			int number = (int)str.charAt(i) - 48;
			if (number == 0) {
				continue;
			}
			numberPosition.x = i % 9;
			numberPosition.y = i / 9;
			SudokuNumber sudokuNumber = new SudokuNumber(numberPosition, number, true);
			sudokuMatrix.addSudokuNumber(sudokuNumber);
		}
	}

	private void paintPlayGrid() {
		this.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
		
		for (int i = 0; i < 3; i++) {
			Rectangle vRectangle = new Rectangle(i * 151, 0, 2, SudokuUtil.PLAY_FIELD_WIDTH);
			vRectangle.setFill(Color.BLACK);
			vRectangle.setStroke(Color.BLACK);
			getChildren().add(vRectangle);
			Line vLine0 = new Line(i * 151 + 51, 0, i * 151 + 51, SudokuUtil.PLAY_FIELD_WIDTH);
			vLine0.setFill(Color.BLACK);
			getChildren().add(vLine0);
			Line vLine1 = new Line(i * 151 + 101, 0, i * 151 + 101, SudokuUtil.PLAY_FIELD_WIDTH);
			vLine1.setFill(Color.BLACK);
			getChildren().add(vLine1);

			Rectangle hRectangle = new Rectangle(0, i * 151, SudokuUtil.PLAY_FIELD_WIDTH, 2);
			hRectangle.setFill(Color.BLACK);
			hRectangle.setStroke(Color.BLACK);
			getChildren().add(hRectangle);
			Line hLine0 = new Line(0, i * 151 + 51, SudokuUtil.PLAY_FIELD_WIDTH, i * 151 + 51);
			hLine0.setFill(Color.BLACK);
			getChildren().add(hLine0);
			Line hLine1 = new Line(0, i * 151 + 101, SudokuUtil.PLAY_FIELD_WIDTH, i * 151 + 101);
			hLine1.setFill(Color.BLACK);
			getChildren().add(hLine1);
		}
		Rectangle vLastRectangle = new Rectangle(453, 0, 2, SudokuUtil.PLAY_FIELD_WIDTH);
		vLastRectangle.setFill(Color.BLACK);
		vLastRectangle.setStroke(Color.BLACK);
		getChildren().add(vLastRectangle);
		Rectangle hLastRectangle = new Rectangle(0, 453, SudokuUtil.PLAY_FIELD_WIDTH, 2);
		hLastRectangle.setFill(Color.BLACK);
		hLastRectangle.setStroke(Color.BLACK);
		getChildren().add(hLastRectangle);
	}

	private final class SelectEventHandler implements EventHandler<MouseEvent> {
		@Override
		public void handle(MouseEvent event) {
			Point selectedRectLocation = getSelectedRectLocation((int)event.getX(), (int)event.getY());
			selectedRect.reLocation(selectedRectLocation);
			selectedRect.setFill(sudokuMatrix, getSelectedPosition());
			requestFocus();
		}
	}

	private void cleanBoard() {
		sudokuMatrix.removeAllNum();
	}

	private final class TypeNumberEventHandler implements EventHandler<KeyEvent> {
		@Override
		public void handle(KeyEvent event) {
			switch (event.getCode()) {
			case DIGIT1:
			case DIGIT2:
			case DIGIT3:
			case DIGIT4:
			case DIGIT5:
			case DIGIT6:
			case DIGIT7:
			case DIGIT8:
			case DIGIT9:
				drawInputedNumber((int)event.getCode().toString().charAt(5) - 48);
				break;
			case DELETE:
			case BACK_SPACE:
				deleteInputedNumber();
			default:
				break;
			}
		}
	}

	private void deleteInputedNumber() {
		Point point = getSelectedPosition();
		sudokuMatrix.deleteInputedNumber(point);
	}

	private void drawInputedNumber(int i) {
		Point point = getSelectedPosition();
		SudokuNumber sudokuNumber = new SudokuNumber(point, i, false);
		sudokuMatrix.addSudokuNumber(sudokuNumber);
	}

	public void addNode(Node eNode) {
		getChildren().add(eNode);
	}

	public void removeNode(Node eNode) {
		eNode.setVisible(false);
		getChildren().remove(eNode);
	}

	private Point getSelectedPosition() {
		Point point = new Point();
		point.x = this.selectedRect.getRectLocation().x / 50;
		point.y = this.selectedRect.getRectLocation().y / 50;

		return point;
	}
	
	private Point getSelectedRectLocation(int layoutX, int layoutY) {
		Point location = new Point();

		location.x = layoutX / 50 * 50 + layoutX / 50 / 3 + 2;
		location.y = layoutY / 50 * 50 + layoutY / 50 / 3 + 2;
		return location;
	}

	private void setNumberVisible(boolean visible) {
		sudokuMatrix.setNumberVisible(visible);
	}

	private void doAddAndStartGame() {
		cleanBoard();
		addSudokuNumberFromString(game.getGameStr());
		backgroundSoundPlayer.play();
	}

	private void whereStartNewGame() {
		Alert gameCompletedAlert = new Alert(AlertType.CONFIRMATION,
				null,
				new ButtonType("Start New Game", ButtonData.OK_DONE),
				new ButtonType("Exit", ButtonData.CANCEL_CLOSE));
		gameCompletedAlert.setTitle("game completed!");
		gameCompletedAlert.setHeaderText("Sudoku is completed! Congratulation!\nWould you like to start a new sudoku?");
		gameCompletedAlert.setContentText("Just follow your heart...");
		Optional<ButtonType> rs = gameCompletedAlert.showAndWait();
		if (rs.get().getButtonData().equals(ButtonData.OK_DONE)) {
			GameController.getGameController().pauseGame();
			ChooseGame.getChooseGame().show();
			ControlButtonPane.getControlButtonPane().setAllButtonDisable (true);
		} else {
			GameController.getGameController().endGame();
		}
	}

	@Override
	public void startGame(Sudoku sudoku) {
		game = sudoku;
		doAddAndStartGame();
	}

	@Override
	public void pauseGame() {
		setNumberVisible(false);
		backgroundSoundPlayer.pause();
	}

	@Override
	public void restartGame() {
		doAddAndStartGame();
	}

	@Override
	public void endGame() {
		backgroundSoundPlayer.stop();
	}

	@Override
	public void completeGame() {
		backgroundSoundPlayer.stop();
		selectedRect.setVisible(false);
		whereStartNewGame();
	}

	@Override
	public void resumeGame() {
		setNumberVisible(true);
		backgroundSoundPlayer.play();
	}
}
